package com.xxjh.brs.map.utils;

/**
 * @author CHENCHEN
 * @title a
 * @company 新兴际华科技（天津）有限公司
 * @create 2024-06-12 8:42
 */
public class TileToLatLongConverter {
    // 瓦片的像素尺寸（常见的Web Mercator瓦片大小为256x256像素）
    private static final int TILE_SIZE = 256;

    // 地球半径（单位：米）
    private static final double EARTH_RADIUS = 6378137.0;

    // 初始偏移量（用于将经纬度转换为像素坐标）
    private static final double INITIAL_RESOLUTION = TILE_SIZE * 0.5 / (Math.PI * EARTH_RADIUS);

    /**
     * 将经纬度转换为像素坐标。
     *
     * @param lat 纬度（范围：-90到90）
     * @param lng 经度（范围：-180到180）
     * @param zoom 缩放级别
     * @return 像素坐标（px, py）
     */
    private static double[] latLngToPixel(double lat, double lng, int zoom) {
        double sinLat = Math.sin(lat * Math.PI / 180);

        // 墨卡托投影的公式
        double pixelX = ((lng + 180) / 360) * TILE_SIZE * Math.pow(2, zoom);
        double pixelY = (1 + Math.sin(lat * Math.PI / 180)) / 2 * TILE_SIZE * Math.pow(2, zoom);

        return new double[]{pixelX, pixelY};
    }

    /**
     * 将像素坐标转换为经纬度。
     *
     * @param px 像素X坐标
     * @param py 像素Y坐标
     * @param zoom 缩放级别
     * @return 经纬度（lat, lng）
     */
    private static double[] pixelToLatLng(double px, double py, int zoom) {
        double n = Math.PI - (2 * Math.PI * py) / (TILE_SIZE * Math.pow(2, zoom));

        double lat = (Math.toDegrees(Math.atan(Math.sinh(n))));
        double lng = (px / (TILE_SIZE * Math.pow(2, zoom))) * 360 - 180;
//        System.out.println(lat +"    "+ lng);
        return new double[]{lat, lng};
    }

    /**
     * 计算瓦片边界的经纬度范围。
     *
     * @param tileX 瓦片X坐标
     * @param tileY 瓦片Y坐标
     * @param zoom 缩放级别
     * @return 瓦片边界的经纬度范围（minLat, minLng, maxLat, maxLng）
     */
    public static double[] calculateTileBounds(int tileX, int tileY, int zoom) {
        // 计算瓦片左上角的经纬度
        double[] topLeftLatLng = pixelToLatLng(tileX * TILE_SIZE, tileY * TILE_SIZE, zoom);
        double minLat = topLeftLatLng[0];
        double minLng = topLeftLatLng[1];

        // 计算瓦片右上角的经纬度
        double[] topRightLatLng = pixelToLatLng((tileX + 1) * TILE_SIZE, tileY * TILE_SIZE, zoom);
        double maxLat = topRightLatLng[0];
        double maxLng = topRightLatLng[1];

        // 计算瓦片左下角的经纬度
        double[] bottomLeftLatLng = pixelToLatLng(tileX * TILE_SIZE, (tileY + 1) * TILE_SIZE, zoom);
        double bottomLat = bottomLeftLatLng[0];

        // 瓦片在纬度上不是等宽的，因此我们需要取左下角和左上角的纬度中的最小值作为minLat
        minLat = Math.min(minLat, bottomLat);

        // 瓦片在经度上是等宽的，所以maxLng已经在计算右上角时得到

        // 返回瓦片边界的经纬度范围
        return new double[]{minLat, minLng, maxLat, maxLng};
    }

    public static void main(String[] args) {
        double[] doubles = calculateTileBounds(423, 195, 9);
        System.out.println("minLat: "+doubles[0]+", minLng: "+doubles[1]+", maxLat: "+doubles[2]+", maxLng: "+doubles[3]);
        /*double[] doubles = latLngToPixel(-90, 180, 5);
        System.out.println(doubles[0] + ", " + doubles[1]);;*/
        /*sout(3368, 1549, 12,calculateTileBounds(3368, 1549, 12));
        sout(6737, 3098, 13,calculateTileBounds(6737, 3098, 13));
        sout(53901, 24785, 16,calculateTileBounds(53901, 24785, 16));
        sout(1724854, 793148, 21,calculateTileBounds(53901, 24785, 16));*/
    }

    private static void sout(int tileX, int tileY, int zoom, double[] bounds) {
        System.out.println(tileX+" "+tileY+" "+zoom+" "+bounds[0]+" "+bounds[1]+" "+bounds[2]+" "+bounds[3]);
    }


    //  转回去
    public static String getTileNumber(final double lat, final double lon, final int zoom) {
        int xtile = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
        int ytile = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1 << zoom));
        if (xtile < 0)
            xtile = 0;
        if (xtile >= (1 << zoom))
            xtile = ((1 << zoom) - 1);
        if (ytile < 0)
            ytile = 0;
        if (ytile >= (1 << zoom))
            ytile = ((1 << zoom) - 1);
        return ("" + zoom + "/" + xtile + "/" + ytile);
    }
}